﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Collections.Generic;
using System.Xml.Linq;

namespace XmlVisualizer.DiagramDesigner.Controls
{
    /// <summary>
    /// Represents the object which causes other objects to be draggable.
    /// </summary>
    public class DragThumb : Thumb
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the DragThumb class.
        /// </summary>
        public DragThumb()
        {
            // add new event handler for the DragDelta event
            base.DragDelta += new DragDeltaEventHandler(DragThumb_DragDelta);
        }
        #endregion

        #region Methods
        public void UpdateOrder()
        {
            DesignerItem designerItem = this.DataContext as DesignerItem; // reference to the item on the canvas
            DesignerCanvas canvas = VisualTreeHelper.GetParent(designerItem) as DesignerCanvas; // reference to the canvas

            if (designerItem != null && canvas != null && designerItem.IsSelected)
            {
                // select all selected DesignerItems
                var designerItems = canvas.selectionService.CurrentSelection.OfType<DesignerItem>();

                foreach (DesignerItem item in designerItems)
                {
                    Connector topConnector = item.GetConnector(Connectors.Top); // get the top Connector of the DesignerItem
                    if (topConnector.Connections.Count > 1)
                    {
                        throw new Exception("Child has more than one parent!");
                    }
                    else if (topConnector.Connections.Count == 1)
                    { // find its parent
                        Connection con = topConnector.Connections[0];
                        DesignerItem parent = con.Source.ParentDesignerItem;

                        Connector bottomConnector = parent.GetConnector(Connectors.Bottom); // get parent's bottom connector

                        List<DesignerItem> children = new List<DesignerItem>(); // get parent's child nodes
                        foreach (Connection c in bottomConnector.Connections)
                        {
                            children.Add(c.Sink.ParentDesignerItem);
                        }

                        // get Order numbers, sort them
                        var orders = (from child in children select child.Info.Order).OrderBy(order => order);

                        // sort them according to their position
                        children.Sort((Comparison<DesignerItem>)((DesignerItem item1, DesignerItem item2) =>
                        {
                            if (Canvas.GetLeft(item1) < Canvas.GetLeft(item2))
                                return -1;
                            else if (Canvas.GetLeft(item1) == Canvas.GetLeft(item2))
                                return 0;
                            else
                                return 1;
                        }));

                        // assign Order numbers to DesignerItems
                        for (int i = 0; i < orders.Count(); ++i)
                        {
                            children[i].Info.Order = orders.ElementAt(i);
                        }

                        // get neccessary items for controller so it can update model
                        XElement parentElement = parent.Element;
                        List<XNode> childNodes = new List<XNode>();
                        foreach (DesignerItem i in children)
                        {
                            childNodes.Add(i.Node);
                        }

                        Document.CurrentController.UpdateOrder(parentElement, childNodes);
                    }
                }
                canvas.InvalidateMeasure();
            }
        }
        #endregion

        #region Event handlers
        protected override void OnMouseUp(System.Windows.Input.MouseButtonEventArgs e)
        {
            base.OnMouseUp(e);

            UpdateOrder();

            e.Handled = true;
        }

        /// <summary>
        /// DragDeltaEvent handler.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        void DragThumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            DesignerItem designerItem = this.DataContext as DesignerItem; // reference to the item on the canvas
            DesignerCanvas canvas = VisualTreeHelper.GetParent(designerItem) as DesignerCanvas; // reference to the canvas
            if (designerItem != null && canvas != null && designerItem.IsSelected)
            {
                double minLeft = double.MaxValue;
                double minTop = double.MaxValue;

                // we only move DesignerItems
                var designerItems = canvas.selectionService.CurrentSelection.OfType<DesignerItem>();

                foreach (DesignerItem item in designerItems)
                {
                    double left = Canvas.GetLeft(item);
                    double top = Canvas.GetTop(item);

                    minLeft = double.IsNaN(left) ? 0 : Math.Min(left, minLeft);
                    minTop = double.IsNaN(top) ? 0 : Math.Min(top, minTop);
                }

                double deltaHorizontal = Math.Max(-minLeft, e.HorizontalChange);
                double deltaVertical = Math.Max(-minTop, e.VerticalChange);

                foreach (DesignerItem item in designerItems)
                {
                    double left = Canvas.GetLeft(item);
                    double top = Canvas.GetTop(item);

                    if (double.IsNaN(left))
                        left = 0;
                    if (double.IsNaN(top))
                        top = 0;

                    Canvas.SetLeft(item, left + deltaHorizontal);
                    Canvas.SetTop(item, top + deltaVertical);

                    item.Info.Position = new Point(left + deltaHorizontal, top + deltaVertical); // update DesignerItem's position in XNodeInfo
                }

                canvas.InvalidateMeasure();
                e.Handled = true;
            }
        }
        #endregion
    }
}
